#ifndef AST_H
#define AST_H

#include "all.h"
#include "Value.h"
#include "ProgramElem.h"
#include "SymTabEntry.h"

class BlockEntry;
class EFSA;
class EventEntry;
class FunctionEntry;
class LabelEntry;
class OpNode;
class PatNode;
class PrimitivePatNode;
class RuleNode;
class SymTabEntry;
class VariableEntry;
class WhileLoopEntry;
class ImmCodes;
class ImmCode;

/*****************************************************************************
  Here is the class hierarchy:
  ProgramElem
  |
  AstNode
  +--------------------------------------------+----------------+
  |               |                 |                           |
  BasePatNode      ExprNode          RuleNode                    StmtNode
  |               |                                             |
  |               |                                             |
  |      +---------+----------+-----------+                     |
  |      |         |          |           |                     |
  |  RefExprNode  OpNode  ValueNode  InvocationNode             |
  |                                                             |
  |                       +--------------+---------------+------+-----+----------+------------+-------------+
  |                       |              |               |            |          |            |             |
  |                 ReturnStmtNode  ExprStmtNode   CompoundStmtNode IfNode   WhileNode   BreakStmtNode  PrintStmtNode
  |
  |
  +-+------------------+
  |                    |
  |                    |
  PrimitivePatNode    PatNode

 ******************************************************************************/

class AstNode: public ProgramElem {
 public:
  enum NodeType  {
    PAT_NODE,
    EXPR_NODE,
    REACTION_NODE,
    STMT_NODE,
    SEQUENCE_NODE,
    RULE_NODE
  };

 public:
  AstNode(NodeType nt, int line=0, int column=0, string file="");
  AstNode(const AstNode&); // copy constructor
  //virtual ~AstNode() {};

  NodeType nodeType() const { return nodeType_;}

  virtual const Type* typeCheck()=0;
  virtual void print(ostream& os, int indent=0) const=0;
  virtual void typePrint(ostream& os, int indent=0) const=0;
  virtual void renameRV(string prefix) {}; // new names start with given prefix
		virtual bool operator==(const AstNode&) const { return false; };
		virtual bool operator!=(const AstNode& a) const
		{ return !operator==(a); };

 private:
  NodeType nodeType_;
  const AstNode* operator=(const AstNode& other); /* disable asg */
};

inline ostream& operator<<(ostream& os, const AstNode& an) {
  an.print(os);
  return os;
};

/****************************************************************/
class ExprNode: public AstNode {
 public:
  enum ExprNodeType   {
  	/* REF_EXPR_NODE, VALUE_NODE and TEMP_NODE will be the name
		of 3 address code */
    REF_EXPR_NODE,
    OP_NODE,
    VALUE_NODE,
    INV_NODE,
	TEMP_NODE	/* temp name place holder (3 address code) */
   };

 public:
  ExprNode(ExprNodeType et, const Value* val=0, int line=0, int column=0,
		   string file=""); // val is saved, but not copied
  ExprNode(const ExprNode&);
  virtual ~ExprNode() {};

  virtual ExprNode* clone() const=0;

  ExprNodeType exprNodeType() const { return exprType_;};
  void exprNodeType(ExprNodeType t) { exprType_ = t; };

  const Value* value() const { return val_; }

  const Type* coercedType() const { return coercedType_; }
  void coercedType(const Type* type) { coercedType_ = type; }

  const Type* typeCheck()=0;
  void print(ostream& os, int indent=0) const=0;
  void typePrint(ostream& os, int indent=0) const=0;
  virtual ExprNode* addr();
  virtual void printVarOnly(ostream& os, int indent) { }

 private:
  ExprNodeType exprType_;
  const Value *val_; // reference semantics for val_ and coercedType_
  const Type* coercedType_;
};

/****************************************************************/
class TempNode: public ExprNode {
 public:
  TempNode(int line=0, int column=0, string file="");
  ~TempNode() {};

  string addrName() {return name_;}
  string registerName();
  void registerName(string name) {reg_name = name;}
  void typePrint(ostream& os, int indent=0) const;
  void print(ostream& os, int indent=0) const;
  const Type* typeCheck();
  ExprNode* clone() const { return new TempNode(); }
  void printVarOnly(ostream& os, int indent) {}
protected:
  string name_;
  //#ifdef TEST_CODEGEN
  string reg_name; /* register name */
  //#endif
};

/****************************************************************/
class RefExprNode: public ExprNode {
 public:
  RefExprNode(string ext, const SymTabEntry* ste=NULL,
	      int line=0, int column=0, string file="");
  RefExprNode(const RefExprNode&);
  ExprNode* clone() const { return new RefExprNode(*this); }

  ~RefExprNode() {};

  string ext() const { return ext_;};
  void ext(string str) { ext_ = str;};

  const SymTabEntry* symTabEntry() const { return sym_;};
  void symTabEntry(const SymTabEntry *ste)  { sym_ = ste;};

  const Type* typeCheck();
  void print(ostream& os, int indent=0) const;
  ImmCodes* code(bool controlFlow=false);
  void typePrint(ostream& os, int indent=0) const;
  void printVarOnly(ostream& os, int indent) {
        os << ext() <<";";
    }
  string addrName() {return ext_;}
  string registerName();
  void registerName(string newName) {
  	SymTabEntry* entry = (SymTabEntry*)sym_;
  	if (entry) entry->registerName(newName);
  }

 private:
  string ext_;
  const SymTabEntry* sym_;
};

/****************************************************************/

#define MAX_OP_ARITY 2


class OpNode: public ExprNode {
 public:
 enum OpCode {
    UMINUS, PLUS, MINUS, MULT, DIV, MOD,
    EQ, NE, GT, LT, GE, LE,
    AND, OR, NOT,
    BITNOT, BITAND, BITOR, BITXOR, SHL, SHR,
    ASSIGN, PRINT, INVALID
  };
  enum OpPrintType {PREFIX, INFIX, POSTFIX};
  struct OpInfo {
    OpCode code_;
    const char* name_;
    int arity_;
	int needParen_;
    OpPrintType prtType_;
    Type::TypeTag argType_[3];
    // operators with > 2 args can be supported
    // only if types of args k through N are identical, for 1 <= k <= 3,
    // and are given by argType[k-1]
    Type::TypeTag outType_;
	const char *typeConstraints_;

	Type::TypeTag argType(int i) {
	  if (i >= arity_) return Type::ERROR;
	  else if (i > 2) return argType_[2];
	  else return argType_[i];
	}
  };

 public:
  static const int VARIABLE = 100;
  static const OpInfo opInfo[];
 public:
  OpNode(OpCode op, ExprNode *l, ExprNode *r=NULL,
	 int line=0, int column=0, string file="");
  OpNode(const OpNode&);
  static string OpCodeStr(OpCode code);
  ExprNode* clone() const { return new OpNode(*this); }
  ~OpNode() {};

  OpCode opCode() const { return opCode_;};
  const ExprNode* arg(unsigned int i) const
    { return (i < arg_.size())? arg_[i] : NULL; };
  unsigned int arity() const { return arity_; }

  void opCode(OpCode a) { opCode_ = a; };
  ExprNode* arg(unsigned int i)
    { return (i < arg_.size())? arg_[i] : NULL; };
  vector<ExprNode*>* args()
    { return &arg_; }

  const Type* typeCheck();
  void print(ostream& os, int indent=0) const;
  void typePrint(ostream& os, int indent=0) const;
  ImmCodes* code(bool controlFlow=false);
  void printVarOnly(ostream& os, int indent) {

      if(arg(0))
        arg(0)->printVarOnly(os,indent);
      if(arg(1))
        arg(1)->printVarOnly(os,indent);

    }


 private:
  unsigned int arity_;
  OpCode   opCode_;
  vector<ExprNode*> arg_;
};

/****************************************************************/

class ValueNode: public ExprNode {
 public:
  ValueNode(Value* val=0, int line=0, int column=0, string file="")
    : ExprNode(ExprNode::VALUE_NODE, val, line,column,file) {
    if (val != NULL) type((Type*)(val->type()));
	addr_ = this;
  }
  ValueNode(const ValueNode& val): ExprNode(val) {};
  ExprNode* clone() const { return new ValueNode(*this); }
  ~ValueNode() {};

  const Type* typeCheck();
  string addrName() {
  	ostringstream os;
	Type* tp = (Type*)coercedType();
	if (tp==NULL) {
		tp = type();
	}
	if (Type::isBool(tp->tag())) {
		os << value()->bval();
	} else {
		if (Type::isFloat(tp->tag())) {
		  	os.precision(1);
		  	os.setf(ios::fixed,ios::floatfield);
		  	os << value()->dval();
		}
		else {
			value()->print(os,0);
		}
	}
	return os.str();
  }
  string registerName() {
  	return addrName();
  }
  void print(ostream& os, int indent=0) const;
  ImmCodes* code(bool controlFlow=false);
  void typePrint(ostream& os, int indent=0) const;
  void printVarOnly(ostream& os, int indent) {}

 private:
  /* val_ field is already included in ExprNode, so no new data members */
};

/****************************************************************/

class InvocationNode: public ExprNode {
  // Used to represent function invocation
 public:
  InvocationNode(const SymTabEntry *ste, vector<ExprNode*>* param=0,
		 int line=0, int column=0, string file="");
  InvocationNode(const InvocationNode&);
  ExprNode* clone() const  { return new InvocationNode(*this); }
  ~InvocationNode() {};

  const SymTabEntry* symTabEntry() const { return ste_; };

  int  arity() const {
	if (params_ == NULL)
	  return 0;
	else return params_->size();
  }
  virtual ExprNode* addr();

  const vector<ExprNode*>* params() const { return params_;};
  vector<ExprNode*>* params() { return params_;}
  void params(vector<ExprNode*>* args){ params_ = args;};
  const ExprNode* param(unsigned int i) const
    { return (params_ != NULL && i < params_->size())? (const ExprNode*)((*params_)[i]) : NULL; };
  ExprNode* param(unsigned int i)
    { return (ExprNode*)((const InvocationNode*)this->param(i));}
  void param(ExprNode* arg, unsigned int i)
    { if (params_ != NULL && i < params_->size()) (*params_)[i] = arg;};

  const Type* typeCheck();
  void print(ostream& os, int indent=0) const;
  ImmCodes* code(bool controlFlow=false);
  void typePrint(ostream& os, int indent=0) const;
  void printVarOnly(ostream& os, int indent){
      if(!params_)
      return;
      for(int i=0;i<params_->size();i++) {
            if((*params_)[i])
            (*params_)[i]->printVarOnly(os,0);
      }
    }

 private:
  vector<ExprNode*>* params_;
  const SymTabEntry *ste_; // reference semantics
};

/****************************************************************/
// There are 3 kinds of PatNodes:
//   PrimitivePatNodes are of the form: event|cond
//   PatNodes consist of PrimitivePatNodes or PatNodes composed with
//      ":", "||" and "*" operators

class BasePatNode: public AstNode {
 public:
  enum PatNodeKind {PRIMITIVE, EMPTY, NEG, SEQ, OR, STAR, UNDEFINED};

 public:
  BasePatNode(PatNodeKind pk, int ln=0, int col=0, string f=""):
	AstNode(PAT_NODE, ln, col, f) {
	parent_ = NULL; root_ = NULL; patKind_ = pk;};
  BasePatNode(const BasePatNode& bpn): AstNode(bpn) {
	patKind_ = bpn.patKind_; parent_ = bpn.parent_; root_ = bpn.root_;}
  ~BasePatNode() {};
  //virtual BasepatNode* clone() const { return new BasePatNode(*this);}


  PatNodeKind kind() const { return patKind_; };
  void kind(PatNodeKind p) {patKind_ = p;}

  const BasePatNode* parent() const { return parent_; }
  BasePatNode* parent() { return parent_;}

  virtual bool hasSeqOps() const=0;
  virtual bool hasNeg() const=0;
  virtual bool hasAnyOrOther() const=0;
  virtual bool isNegatable() const {
	return ((!hasSeqOps()) && (!hasNeg()) && (!hasAnyOrOther())); }
  //added by supreet
 virtual string getEventPattern (string name) const=0;

 virtual BasePatNode* derivativeWRTname(string eventName) const{};


 virtual vector<BasePatNode*>* getNextItemSet(string eventName) const{return new vector<BasePatNode*>() ;};
 virtual string getStringRepresentation() const{
	  ostringstream os;
	  string* representation;
	  int currentPostion;

	  this->print(os);
	  representation = new string(os.str());
	 return *representation;
 }

 virtual bool include_eps() const = 0;
virtual string replaceNotOperator(string names) const=0;
virtual BasePatNode* replaceAny(string universe) { return NULL;}
virtual BasePatNode* replaceNeg(string universe){
	return NULL;
}
virtual BasePatNode* getComplement(string universe){
	return NULL;
}


 //for param event matcher
 const ExprNode* cond() const { return cond_; }
 ExprNode* cond() { return cond_; }
 void cond(ExprNode* c) { cond_ = c;}

 private:
  PatNodeKind patKind_;
  BasePatNode* parent_;
  BasePatNode* root_;

  //for param event matcher
  ExprNode* cond_;
};

/****************************************************************/

class PrimitivePatNode: public BasePatNode {
 public:
  PrimitivePatNode(EventEntry* ee, vector<VariableEntry*>* params,
				   ExprNode* c=NULL,
				   int line=0, int column=0, string file="");
  //PrimitivePatNode(const PrimitivePatNode& ppn);
  ~PrimitivePatNode() {};
  //BasePatNode* clone() { return new PrimitivePatNode(*this); }

  const EventEntry* event() const { return ee_; }
  EventEntry* event() { return ee_; }

  const vector<const VariableEntry*>* params() const {
	return (vector<const VariableEntry*>*)params_; }
  vector<VariableEntry*>* params() { return params_; }

  const ExprNode* cond() const { return cond_; }
  ExprNode* cond() { return cond_; }
  void cond(ExprNode* c) { cond_ = c;}

  ExprNode* condition() { return condition_; }
  const ExprNode* condition() const { return condition_; }

  const list<const OpNode*>& asgs() const {
	return (list<const OpNode*>&)asgs_; }
  list<OpNode*>& asgs() { return asgs_; }

  bool hasSeqOps() const;
  bool hasNeg() const;
  bool hasAnyOrOther() const;

  const Type* typeCheck();
  void print(ostream& os, int indent=0) const;
  void typePrint(ostream& os, int indent=0) const;
   //added by supreet
  string getEventPattern(string name) const;
  string replaceNotOperator(string names) const;
  BasePatNode* replaceNeg(string universe){
	  return this;
  }
  BasePatNode* replaceAny(string universe){
      if(hasAnyOrOther())
        return this->getComplementForAny(universe);
      else
      return this;

  }
  BasePatNode* getComplement(string universe);

  BasePatNode* getComplementForAny(string universe);

  BasePatNode* derivativeWRTname(string eventName) const;

  vector<BasePatNode*>* getNextItemSet(string eventName) const;

  bool include_eps() const{
 	 return false;
  }

 private:

  EventEntry* ee_;
  vector<VariableEntry*>* params_;
  /* cond_ may contain assignments as well as other expressions */
  /* condition_ contains all expresions in cond_ other than assignments */
  ExprNode* cond_;
  ExprNode* condition_;
  list<OpNode*> asgs_;
};

/****************************************************************/
class PatNode: public BasePatNode {
 public:
  PatNode(int line=0, int column=0, string file="");
  PatNode(PatNodeKind pk, BasePatNode *p1, BasePatNode*p2=NULL, int line=0, int column=0, string file="");

  ~PatNode() {};
  //AstNode* clone()
  //  { return new PatNode(*this); }

  const BasePatNode* pat1() const { return pat1_; }
  BasePatNode* pat1() { return pat1_; }
  const BasePatNode* pat2() const { return pat2_; }
  BasePatNode* pat2() { return pat2_; }

  bool hasNeg() const;
  bool hasSeqOps() const;
  bool hasAnyOrOther() const;

  BasePatNode* getComplement(string universe);

  const Type* typeCheck();
  void print(ostream& os, int indent=0) const;
  void typePrint(ostream& os, int indent=0) const;
  //added by supreet
  string getEventPattern(string name) const;

  string replaceNotOperator(string names) const;

  BasePatNode* replaceNeg(string universe){
	  switch (kind()) {
	  		case BasePatNode::EMPTY:
	  			return this;
	  		case BasePatNode::STAR:
	  			//if (pat1_->hasNeg())
	  			return new PatNode(BasePatNode::STAR, pat1_->replaceNeg(universe));
	  			break;

	  		case BasePatNode::OR:
	  			//return pat1_->include_eps() || pat2_->include_eps();
	  			return new PatNode(BasePatNode::OR, pat1_->replaceNeg(universe), pat2_->replaceNeg(universe));
	  			break;

	  		case BasePatNode::SEQ:
	  			//return pat1_->include_eps() && pat2_->include_eps();
	  			return new PatNode(BasePatNode::SEQ, pat1_->replaceNeg(universe), pat2_->replaceNeg(universe));
	  			break;

	  		case BasePatNode::NEG:
	  			/*if (!pat1_->hasSeqOps()) {c
	  			    return pat1_->getComplement();

	  			}
	  			else {
				cout << "Cannot negate!!!!!!\n\n";
                    return this;
	  			}*/
	  			//if(pat1_->hasNeg())
	  			return pat1_->getComplement(universe);
	  			//else
                //return this;
				break;

	  		default:
	  			cout << "\n\n\ncalled hereeeeee\n\n\n";
	  		break;
	  	}
	  return this;
  }
BasePatNode* replaceAny(string universe){
	  switch (kind()) {
	  		case BasePatNode::EMPTY:
	  			return this;
	  		case BasePatNode::STAR:
	  			//if (pat1_->hasAnyOrOther())
	  			return new PatNode(BasePatNode::STAR, pat1_->replaceAny(universe));
	  			//else
	  			//return pat1_;
	  			break;

	  		case BasePatNode::OR:
	  			//return pat1_->include_eps() || pat2_->include_eps();
	  			return new PatNode(BasePatNode::OR, pat1_->replaceAny(universe), pat2_->replaceAny(universe));
	  			break;

	  		case BasePatNode::SEQ:
	  			//return pat1_->include_eps() && pat2_->include_eps();
	  			//if (pat1_->hasAnyOrOther() && pat2_->hasAnyOrOther())
	  			return new PatNode(BasePatNode::SEQ, pat1_->replaceAny(universe), pat2_->replaceAny(universe));
	  			//else if(
	  			break;

	  		case BasePatNode::NEG:
                //BasePatNode* pat = pat1_->getComplement()
	  			return new PatNode(BasePatNode::NEG, pat1_->replaceAny(universe));
	  			break;
            //case BasePatNode::PRIMITIVE:
	  			//if (pat1_->hasAnyOrOther())
	  				//return pat1_->getComplementForAny(universe);

	  		default:
	  			cout << "\n\n\ncalled hereeeeee\n\n\n";
	  		break;
	  	}
	  return this;
  }

  bool include_eps() const{
	 /* for (int i=0; i<pat->size(); i++)
		  if (pat->at(i)->kind() == BasePatNode::EMPTY)
			  return true;
	  return false;*/
	  switch (kind()) {
		case BasePatNode::EMPTY:
		case BasePatNode::STAR:
			return true;

		case BasePatNode::OR:
			return pat1_->include_eps() || pat2_->include_eps();

		case BasePatNode::SEQ:
			return pat1_->include_eps() && pat2_->include_eps();

		default:
			cout << "\n\n\ncalled hereeeeee\n\n\n";
		break;
	}
  }

  vector<BasePatNode*>* mergeBPnode(vector<BasePatNode*>* bp1, vector<BasePatNode*>* bp2) const{
	  vector<BasePatNode*>* result = new vector<BasePatNode*>();
	  for (int i=0; i<bp1->size(); i++)
		  result->push_back(bp1->at(i));
	  for (int i=0; i<bp2->size(); i++)
		  result->push_back(bp2->at(i));
	  return result;
  }

  vector<BasePatNode*>* mergeBPnode(vector<BasePatNode*>* bp1, BasePatNode* bp2) const{
	  vector<BasePatNode*>* result = new vector<BasePatNode*>();
	  for (int i=0; i<bp1->size(); i++)
		  result->push_back(bp1->at(i));
	  result->push_back(bp2);
	  return result;
  }



  BasePatNode* concateBPnode(BasePatNode* bp1, BasePatNode* bp2) const{

	  if ((bp1 && bp1->kind() == BasePatNode::UNDEFINED) || (bp2 && bp2->kind() == BasePatNode::UNDEFINED))
		  return new PatNode(BasePatNode::UNDEFINED,(BasePatNode*)NULL);
	  if ((bp1 && bp1->kind() == BasePatNode::EMPTY) && (bp2 && bp2->kind() == BasePatNode::EMPTY))
		  return new PatNode(BasePatNode::EMPTY, (BasePatNode*)NULL);
	  if (bp1 && bp1->kind() == BasePatNode::EMPTY)
		  return bp2;
	  else if (bp2 && bp2->kind() == BasePatNode::EMPTY)
		  return bp1;

	  BasePatNode* bp = new PatNode(BasePatNode::SEQ, bp1, bp2);
	  return bp;
  }

  vector<BasePatNode*>* concatBPnodes(vector<BasePatNode*>* bp1, vector<BasePatNode*>* bp2) const{
	  vector<BasePatNode*>* result = new vector<BasePatNode*>();
	  for (int i=0; i<bp1->size(); i++)
		  for (int j=0; j<bp2->size(); j++)
			  result->push_back(concateBPnode(bp1->at(i), bp2->at(j)));

	  return result;
  }

  vector<BasePatNode*>* concatBPnodes(vector<BasePatNode*>* bp1, BasePatNode* bp2) const{
	  vector<BasePatNode*>* result = new vector<BasePatNode*>();
	  for (int i=0; i<bp1->size(); i++)
			  result->push_back(concateBPnode(bp1->at(i), bp2));

	  return result;
  }

  vector<BasePatNode*>* getNextItemSet(string eventName) const{

	  vector<BasePatNode*>* result = new vector<BasePatNode*>();

	  vector<BasePatNode*>* pat1_d, *pat2_d;

	  if (pat1_) pat1_d = pat1_->getNextItemSet(eventName);
	  if (pat2_) pat2_d = pat2_->getNextItemSet(eventName);

	  switch (kind()) {
		case BasePatNode::SEQ:{
			result = mergeBPnode(result, concatBPnodes(pat1_d, pat2_));
			if (pat1_->include_eps())
				result = mergeBPnode(result, pat2_d);
			return result;
			break;
		}
		case BasePatNode::OR:{
			result = mergeBPnode(result, pat1_d);
			result = mergeBPnode(result, pat2_d);
			return result;
			break;
		}
		case BasePatNode::STAR:{
			result = mergeBPnode(result, concatBPnodes(pat1_d, new PatNode(BasePatNode::STAR, pat1_)));
			if (pat1_d->size() == 1 && pat1_d->at(0)->kind() == BasePatNode::EMPTY){
				result = mergeBPnode(result, new PatNode(BasePatNode::EMPTY, (BasePatNode*)NULL));
			}
			return result;
			break;
		}
		case BasePatNode::PRIMITIVE:{
			cout <<"Primitive\n\n";
			break;
		}
		case BasePatNode::UNDEFINED:{
			result->push_back(new PatNode(BasePatNode::UNDEFINED, (BasePatNode*)NULL));
			return result;
			break;
		}
		case BasePatNode::EMPTY:{
			result->push_back(new PatNode(BasePatNode::EMPTY, (BasePatNode*)NULL));
			return result;
			break;
		}


		default:
			cout << "Not handled case!!!!!!\n\n\n\n\n";
			return result;
		break;
	}

  }


 private:
  PatNode(const PatNode&);

  BasePatNode *pat1_;
  BasePatNode *pat2_;
};


/****************************************************************/

class StmtNode: public AstNode {
 public:
  enum StmtNodeKind { ILLEGAL=-1, EXPR, IF, COMPOUND, RETURN, WHILE, BREAK, PRINT};
 public:
  StmtNode(StmtNodeKind skm, int line=0, int column=0, string file=""):
	AstNode(AstNode::STMT_NODE, line,column,file) { skind_ = skm; };
  ~StmtNode() {};
  //AstNode* clone()
  //  { return new StmtNode(*this); }

  StmtNodeKind stmtNodeKind() const { return skind_;}

  void print(ostream& os, int indent) const = 0;
  void typePrint(ostream& os, int indent=0) const=0;
  const Type* typeCheck() = 0;

 private:
  StmtNodeKind skind_;
};

/****************************************************************/

class ReturnStmtNode: public StmtNode {
 public:
  ReturnStmtNode(ExprNode *e, FunctionEntry* fe,
				 int line=0, int column=0, string file=""):
    StmtNode(StmtNode::RETURN,line,column,file) { expr_ = e; fun_ = fe;};
  ~ReturnStmtNode() {};

  const Type* typeCheck();
  ImmCodes* code(bool controlFlow=false);
  void print(ostream& os, int indent) const {
	os << "return ";
	if(expr_ != NULL) expr_->print(os, indent); else os << "NULL";};
  void typePrint(ostream& os, int indent=0) const {
	os << "return ";
	if(expr_ != NULL) expr_->typePrint(os, indent); /*else os << "NULL";*/};
   ExprNode*  expr() { return expr_;}
 private:
  ExprNode* expr_;
  FunctionEntry* fun_;
};

/****************************************************************/

class ExprStmtNode: public StmtNode {
 public:
  ExprStmtNode(ExprNode* e, int line=0, int column=0, string file=""):
    StmtNode(StmtNode::EXPR,line,column,file) { expr_ = e; };
  ~ExprStmtNode() {};
  //AstNode* clone()
  //  { return new ExprStmtNode(*this); }

  const Type* typeCheck();
  void print(ostream& os, int indent) const {
	if (expr_ != NULL) { expr_->print(os, indent);}};
  void typePrint(ostream& os, int indent) const {
	if (expr_ != NULL) { expr_->typePrint(os, indent);}};
  ImmCodes* code(bool controlFlow=false);
  ExprNode* expr() {
      return expr_;
  }
 private:
  ExprNode* expr_;
};

/****************************************************************/

class CompoundStmtNode: public StmtNode{
 public:
  CompoundStmtNode(list<StmtNode*> *Slist, int ln=0, int col=0, string fl=""):
	StmtNode(StmtNode::COMPOUND, ln,col,fl) { stmts_ = Slist;};
  ~CompoundStmtNode() { };
  //AstNode* clone()
  //  { return new CompoundStmtNode(*this); }

  const list<StmtNode*>* stmts() const { return stmts_;}

  list<StmtNode*>* stmts() { return stmts_;}
  void add(StmtNode *s)
    { if(stmts_ != NULL) stmts_->push_back(s); };

    void stmts(list<StmtNode*> *Slist) {
        stmts_ = Slist;
    }

  const Type* typeCheck();
  void  printWithoutBraces(ostream& os, int indent) const;
  void  typePrintWithoutBraces(ostream& os, int indent) const;
  ImmCodes* code(bool controlFlow=false);
  void  print(ostream& os, int indent) const;
  void  typePrint(ostream& os, int indent) const;
  bool	isLastStatementReturn();

 private:
  CompoundStmtNode(const CompoundStmtNode&);

  list<StmtNode*>   *stmts_;
};

/****************************************************************/

class IfNode: public StmtNode{
 public:

  IfNode(ExprNode* cond, StmtNode* thenStmt,
		 StmtNode* elseStmt=NULL, int line=0, int column=0, string file="");
  ~IfNode(){};
  //AstNode* clone()
  //  { return new IfNode(*this); }

  const ExprNode* cond() const {return cond_;}
  const StmtNode* elseStmt() const { return else_;};
  const StmtNode* thenStmt() const  { return then_;};

  ExprNode* cond() {return cond_;}
  StmtNode* elseStmt() { return else_;};
  StmtNode* thenStmt() { return then_;};

  const Type* typeCheck();
  void print(ostream& os, int indent) const;
  ImmCodes* code(bool controlFlow=false);
  void  typePrint(ostream& os, int indent) const;

 private:
  ExprNode *cond_;
  StmtNode *then_, *else_;

  IfNode(const IfNode&);
};

/****************************************************************/

class RuleNode: public AstNode {
 public:
  RuleNode(BlockEntry *re, BasePatNode* pat, StmtNode* reaction,
	   int line=0, int column=0, string file="");
  ~RuleNode() {};
  //AstNode* clone()
  //  { return new RuleNode(*this); }

  const BlockEntry* ruleEntry() const { return rste_; };
  BlockEntry* ruleEntry() { return rste_; };

  const BasePatNode* pat() const { return pat_; };
  BasePatNode* pat() { return pat_; };

  const StmtNode* reaction() const { return reaction_; };
  StmtNode* reaction() { return reaction_; };

  const Type* typeCheck();
  void print(ostream& os, int indent=0) const;
  void  typePrint(ostream& os, int indent) const;
  ImmCodes* code(bool controlFlow=false);
  FunctionEntry* func() {return func_;}

 private:
  BlockEntry    *rste_;
  BasePatNode *pat_;
  StmtNode *reaction_;
  static int ruleInstances_;
  FunctionEntry *func_;	/* for action */

  RuleNode(const RuleNode&);
};

/****************************************************************/
/* WhileStmt Node added */
class WhileNode: public StmtNode{
	public:

		WhileNode(ExprNode* cond, StmtNode* whileStmt,
				int line=0, int column=0, string file="");
		~WhileNode(){};

		const ExprNode* cond() const {return cond_;}
		const StmtNode* whileStmt() const { return stmt_;};

		ExprNode* cond() {return cond_;}
		StmtNode* whileStmt() { return stmt_;};
		void addLoopInvStmt(vector<StmtNode*> *s) { loop_inv_stmt_ = s;}

		void print(ostream& os, int indent) const;
  		ImmCodes* code(bool controlFlow=false);
		const Type* typeCheck();
        void  typePrint(ostream& os, int indent) const;

	private:
		ExprNode *cond_;
		StmtNode *stmt_;
		vector<StmtNode*> *loop_inv_stmt_;

		WhileNode(const WhileNode&);
};

/****************************************************************/
/* BreakStmtNode added */

class BreakStmtNode: public StmtNode {
	public:
		BreakStmtNode(WhileLoopEntry* enclosedWhileLoop, Value* loop_num=NULL,
				int line=0, int column=0, string file=""):
			StmtNode(StmtNode::BREAK,line,column,file) { loop_num_ = loop_num; enclosedWhileLoop_ = enclosedWhileLoop;};
		~BreakStmtNode() {};

		void print(ostream& os, int indent) const;
  		ImmCodes* code(bool controlFlow=false);
		const Type* typeCheck();
        void  typePrint(ostream& os, int indent) const;

	private:
			Value* loop_num_;
			WhileLoopEntry* enclosedWhileLoop_;
};

/****************************************************************/
/* print stmt added */

/*class PrintStmtNode: public StmtNode {
    public:
    PrintStmtNode(ExprNode* cond, int line=0, int column=0, string file=""):
        StmtNode(StmtNode::PRINT,line,column,file) { cond_ = cond;};
		~PrintStmtNode() {};

        void print(ostream& os, int indent) const;
        const Type* typeCheck();
        ImmCodes* code(bool controlFlow=false);
        void  typePrint(ostream& os, int indent) const;
        const ExprNode* cond() const {return cond_;}


    ExprNode* cond() {return cond_;}
    private:
    ExprNode* cond_;

};*/
/****************************************************************/
#endif
